//
// Created by zyw on 23-2-23.
//

#include <unistd.h>

#include "cm_thread_task_manager.h"
#include "rt_print.h"

CMThreadTaskManager::CMThreadTaskManager(int maxThreads)
{
    maxThreadsSupport = maxThreads;
    curThreads        = 0;
    exited            = false;
    mainPid           = 0;
}

CMThreadTaskManager::~CMThreadTaskManager() {}

void *thread_run(void *args)
{
    rtdzlog_debug("thread_run");
    CMThreadTaskManager *that = (CMThreadTaskManager *)args;
    if (!that)
    {
        rterror("thread null args");
        return NULL;
    }
    that->runSelf();
    rtinfo("thread exit");
    return NULL;
}

int CMThreadTaskManager::start()
{
    if (mainPid > 0)
    {
        return 0;
    }
    rtinfo("start");
    exited  = false;
    int ret = pthread_create(&mainPid, NULL, thread_run, this);
    if (ret < 0)
    {
        rterror("pthread create fail ret = [%d]", ret);
        return ret;
    }
    return ret;
}

int CMThreadTaskManager::stop()
{
    rtinfo("stop ");
    exited = true;
    if (mainPid > 0)
    {
        pthread_join(mainPid, NULL);
    }
    mainPid = 0;
    return 0;
}

int CMThreadTaskManager::addToThread(CMThreadRunImpl *impl, std::string &name, int runAlone)
{
    rtdebug("add one thread name=[%s] runAlone=[%d]", name.c_str(), runAlone);
    lock.lock(__func__, __LINE__);
    if (runAlone)
    {
        impl->threadItem.name = name;
        int ret               = runInNewThread(impl);
        if (ret < 0)
        {
            rterror("run in new thread fail ret=[%d]", ret);
            lock.unlock();
            return ret;
        }
        else
        {
            threadAloneTemp.push_back(impl);
            lock.unlock();
            return threadAlone.size() + 1;
        }
    }
    else
    {
        impl->threadItem.name = name;
        impl->threadItem.pid  = mainPid;
        threadListTemp.push_back(impl);
        lock.unlock();
        return threadList.size() + 1;
    }
    return 0;
}

int CMThreadTaskManager::addToThread(CMThreadRunImpl *impl, const char *name, int runAlone)
{
    std::string _name = name;
    return addToThread(impl, _name, runAlone);
}

int CMThreadTaskManager::runSelf()
{
    while (!exited)
    {
        if (threadList.empty() && threadAlone.empty() && threadListTemp.empty() && threadAloneTemp.empty())
        {
            sleep(1);
            continue;
        }
        lock.lock(__func__, __LINE__);
        for (auto iter = threadListTemp.begin(); iter != threadListTemp.end(); iter++)
        {
            threadList.push_back((*iter));
        }
        threadListTemp.clear();
        for (auto iter = threadAloneTemp.begin(); iter != threadAloneTemp.end(); iter++)
        {
            threadAlone.push_back((*iter));
        }
        threadAloneTemp.clear();
        lock.unlock();

        for (auto iter = threadList.begin(); iter != threadList.end();)
        {
            if ((*iter)->runDone())
            {
                CMThreadRunImpl *impl = *iter;
                delete impl;
                threadList.erase(iter++);
            }
            else
            {
                iter++;
            }
        }
        for (auto iter = threadAlone.begin(); iter != threadAlone.end();)
        {
            if ((*iter)->threadItem.pid == 0)
            {
                CMThreadRunImpl *impl = (*iter);
                delete impl;
                threadAlone.erase(iter++);
            }
            else
            {
                iter++;
            }
        }
        for (auto iter = threadList.begin(); iter != threadList.end(); iter++)
        {
            (*iter)->run();
        };
        usleep(50000);
    }
    return 0;
}

void *run_in_alone(void *args)
{
    CMThreadRunImpl *impl = (CMThreadRunImpl *)args;
    if (!impl)
    {
        return NULL;
    }
    rtinfo("thread [%s] run start", impl->threadItem.name.c_str());
    impl->run();
    impl->threadItem.pid = 0;
    *(impl->p_curThreads)--;
    rtinfo("thread [%s] run done", impl->threadItem.name.c_str());
    return NULL;
}

int CMThreadTaskManager::runInNewThread(CMThreadRunImpl *impl)
{
    if (++curThreads > maxThreadsSupport)
    {
        rterror("threads count reach [%d] max [%d], exit", curThreads, maxThreadsSupport);
        curThreads--;
        return -3;
    }
    impl->p_curThreads = &curThreads;
    rtinfo("create name [%s]", impl->threadItem.name.c_str());
    int ret = pthread_create(&impl->threadItem.pid, NULL, run_in_alone, impl);
    if (ret < 0)
    {
        rterror("thread create fail ret=[%d]", ret);
        return -2;
    }
    pthread_detach(impl->threadItem.pid);
    return 0;
}

void CMThreadTaskManager::debug()
{
    rtdebug(
        "thread list size =[%d] thread alone size =[%d] thread list tmp size = [%d] thread alone tmp size = [%d]",
        threadList.size(), threadAlone.size(), threadListTemp.size(), threadAloneTemp.size());
    for (auto iter = threadList.begin(); iter != threadList.end(); iter++)
    {
        rtdebug("thread list [%s] pid = [%u]", (*iter)->threadItem.name.c_str(), (*iter)->threadItem.pid);
    }
    for (auto iter = threadAlone.begin(); iter != threadAlone.end(); iter++)
    {
        rtdebug("thread alone [%s] pid = [%u]", (*iter)->threadItem.name.c_str(), (*iter)->threadItem.pid);
    }
}
